using System;
using System.Collections.Generic;
using System.Text;
using Heaven.Mathematics;

namespace Heaven.Geometry.Procedural
{
    /// <summary>
    /// This is a class for a construction 
    /// which generates a sphere polygonal object
    /// </summary>
    public class Sphere : IProcedural
    {
        #region Properties

        /// <summary>
        /// Center of the sphere
        /// </summary>
        public Vector3 Center = Vector3.Zero;

        /// <summary>
        /// Radius of the sphere
        /// </summary>
        public double Radius = 5;

        /// <summary>
        /// Tessellation quality. 
        /// This is a relative value. 
        /// Default equals 0.1. 
        /// Better quality can be reached by a smaller value
        /// </summary>
        public double Quality = 0.2;

        /// <summary>
        /// Hemisphere (0..1]
        /// </summary>
        public double Hemisphere = 1.0;

        /// <summary>
        /// Does the sphere have a cap?
        /// </summary>
        public bool HasCap = true;

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor
        /// </summary>
        public Sphere() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="center">Center of the sphere</param>
        /// <param name="radius">Radius of the sphere</param>
        public Sphere(Vector3 center, double radius)
        {
            Center = center;
            Radius = radius;
        }

        #endregion

        #region Generation

        /// <summary>
        /// Generates a polygonal model
        /// </summary>
        /// <returns>A model</returns>
        public Polygonal Generate()
        {
            Tessellator tessellator = new Tessellator();
            tessellator.Function = Function;
            tessellator.DerivativeU = dU;
            tessellator.DerivativeV = dV;
            //tessellator.StartU = Math.PI * 0.5 - (Math.PI * Hemisphere);
            tessellator.StartU = Math.PI * 0.5 - Math.Acos(1.0 - Hemisphere * 2.0);
            tessellator.EndU =   Math.PI * 0.5 - 0.00001;
            tessellator.StartV = 0;
            tessellator.EndV = Math.PI * 2.0;
            tessellator.dU = Quality;
            tessellator.dV = Quality;
            Polygonal polygonal = tessellator.Tesselate();

            if ((HasCap) &&(Hemisphere < 1.0))
            {
                /*Disc cap = new Disc(Vector3.Zero, Math.Sqrt(Radius * Radius - Math.Pow(Radius * Hemisphere * 0.5, 2)));
                cap.Quality = Quality;
                Polygonal capPolygonal = cap.Generate();
                polygonal.AddPolygonal(capPolygonal,
                    Matrix4.RotateYaw(Math.PI)*
                    Matrix4.Translate(Center.X, Center.Y + Radius - 2 * Radius * Hemisphere, Center.Z));*/
                polygonal.AddPolygonal(tessellator.GetCapV(tessellator.StartU, false));
            }

            return polygonal;
        }

        #endregion

        #region Equations

        // Sphere equation
        Vector3 Function(double u, double v)
        {
            Vector3 result = new Vector3();

            result.X = Radius * Math.Cos(u) * Math.Sin(v) + Center.X;
            result.Z = Radius * Math.Cos(u) * Math.Cos(v) + Center.Z;
            result.Y = Radius * Math.Sin(u) + Center.Y;

            return result;
        }

        Vector3 dU(double u, double v)
        {
            Vector3 result = new Vector3();

            result.X = Radius * Math.Cos(u) * Math.Cos(v);
            result.Z = -Radius * Math.Cos(u) * Math.Sin(v);
            result.Y = 0.0f;

            return result;
        }

        Vector3 dV(double u, double v)
        {
            Vector3 result = new Vector3();

            result.X = -Radius * Math.Sin(u) * Math.Sin(v);
            result.Z = -Radius * Math.Sin(u) * Math.Cos(v);
            result.Y = Radius * Math.Cos(u);

            return result;
        }

        #endregion
    }
}
